//:C10:VirtualConstrutor.cpp
#include <cstdlib>
#include <iostream>
#include <string>
#include <stdexcept>
#include <cstddef>
#include <vector>
#include "../purge.h"
using namespace std;

class Shape
{
    Shape* s;
    // Prevent copy-construction & operator=
    Shape(Shape&);
    Shape operator=(Shape&);
protected:
    Shape() { s = 0; }
public:
    virtual void draw() { s->draw(); }
    virtual void erase() { s->erase(); }        
    virtual void test() { s->test(); }
    virtual ~Shape()
    {
        cout << "~Shape" << endl;
        if(s)
        {
            cout << "Making virtual call: ";
            s->erase();
        }

        cout << "delete s: ";
        delete s;    // The polymorphic deletion
        // (delete 0 is legal; it produces a no-op)
    }

    class BadShapeCreation  : public logic_error
    {
    public:
        BadShapeCreation(string type)
        : logic_error("Cannot create type " + type) {}
    };
    Shape(string type) throw(BadShapeCreation);
};

class Circle  : public Shape
{
    Circle(Circle&);
    Circle operator=(Circle&);
    Circle(){}    // Private constructor
    friend class Shape;
public:
    void draw() { cout << "Circle::draw" << endl; }        
    void erase() { cout << "Circle::erase" << endl; }
    void test() { draw(); }
    ~Circle() { cout << "Circle::~Circle" << endl; }
};

class Square : public Shape
{
public:
    Square(Square&);
    Square operator=(Square&);
    Square() {}
    friend class Shape;
public:
    void draw() { cout << "Square::draw" << endl; }            
    void erase() { cout << "Square::erase" << endl; }
    void test() { cout << "Square::~Square" << endl; }
    ~Square() { cout << "Square::~Square" << endl; }
};

Shape::Shape(string type) throw(Shape::BadShapeCreation)
{
    if(type == "Circle")
    {
        s = new Circle;
    }
    else if(type == "Square")
    {
        s = new Square;
    }
    else throw BadShapeCreation(type);
    draw();    // Virtual call in the constructor
}

char* sl[] = { "Circle", "Square", "Square", "Circle", "Circle", "Circle", "Square"};

int main()
{
    vector<Shape*> shapes;
    cout << "virtual constructor calls:" << endl;

    try
    {
        for(size_t  i = 0; i < sizeof sl / sizeof sl[0]; i++)
        {
            shapes.push_back(new Shape(sl[i]));
        }
    }catch(Shape::BadShapeCreation e)
    {
        cout << e.what() << endl;
        purge(shapes);
        return EXIT_FAILURE;
    }

    for(size_t i = 0; i < shapes.size(); i++)
    {
        shapes[i]->draw();
        cout << "test" << endl;
        shapes[i]->draw();
        cout << "test" << endl;
        shapes[i]->test();
        cout << "end test" << endl;
        shapes[i]->erase();
    }

    Shape c("Circle");    // Create on the stack
    cout << "destructor calls: " << endl;
    purge(shapes);
}
